#include <vector>
#include <string>
#include <cmath>
#include <string.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <algorithm>
#include <assert.h>
#include <sys/stat.h>
#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <ext/stdio_filebuf.h>
#include <fcntl.h>
#include "defer.h"
#include "fileio.h"
#include "ojData.h"
#include "log.h"
using namespace std;
#ifdef __linux__
#include <dirent.h>
#include <string.h>
std::vector<std::string> searchFiles(std::string path, std::string pattern, bool (*check)(const string&, const string&))
{
    vector<string> files;
    struct dirent *ptr;
    DIR *dir;
    dir = opendir((path).c_str());
    if (dir == NULL) {
        return files;
    }
    // printf("文件列表:\n");
    while ((ptr = readdir(dir)) != NULL)
    {

        //跳过'.'和'..'两个目录
        if (ptr->d_name[0] == '.')
            continue;
        if (check(ptr->d_name, pattern.c_str()))
            files.push_back(ptr->d_name);
    }
    closedir(dir);
    return files;
}

#endif

#if defined(WIN32) || defined(WIN64)
#include <io.h>
#include <windows.h>
std::vector<std::string> searchFiles(std::string path, std::string pattern, bool (*check)(const string&, const string&))
{
    long handle;
    path = path + "/*";
    struct _finddata_t fileinfo;
    vector<string> files;
    //第一次查找
    handle = _findfirst(path.c_str(), &fileinfo);
    if (handle == -1)
        return files;
    do
    {
        //找到的文件的文件名
        if (fileinfo.name[0] == '.')
            continue;
        if (check(fileinfo.name, pattern.c_str()))
            files.push_back(fileinfo.name);

    } while (!_findnext(handle, &fileinfo));
    _findclose(handle);
    return files;
}

#endif

bool IsSameFile(const char* a, const char* b)
{
    struct stat A;
    struct stat B;
    stat(a, &A);
    stat(b, &B);
    return A.st_dev == B.st_dev && A.st_ino == B.st_ino;

}

bool isSameContentFile(const string& f1, const string& f2, ios::openmode mode)
{
    Log::instance().getError() << f1 << " " << f2 << " compare content" << endl;
    ifstream if1(f1, mode);
    if (!if1.is_open()) {
        Log::instance().getError() << f1 << " open error(" << errno << "):" << strerror(errno) << endl;
        return false;
    }
    defer(
        if1.close();
    );
    ifstream if2(f2, mode);
    if (!if1.is_open()) {
        Log::instance().getError() << f2 << " open error(" << errno << "):" << strerror(errno) << endl;
        return false;
    }
    defer(
        if2.close();
    );
    char buff1[MAX_PIPEBUFF], buff2[MAX_PIPEBUFF];
    memset(buff1, 0, MAX_PIPEBUFF);
    memset(buff2, 0, MAX_PIPEBUFF);
    int ret1, ret2;
    while (!if1.eof() && !if2.eof()) {
        if (if1.read(buff1, MAX_PIPEBUFF - 1)) {
            ret1 = if1.gcount();
            buff1[ret1] = '\0';
        } else {
            ret1 = -1;
        }
        if (if2.read(buff2, MAX_PIPEBUFF - 1)) {
            ret2 = if2.gcount();
            buff2[ret2] = '\0';
        } else {
            ret2 = -1;
        }
        if (ret1 != ret2 || strcmp(buff1, buff2) != 0) {
            Log::instance().getError() << f1 << " " << f2 << " is not same content" << endl;
            return false;
        }
    }
    Log::instance().getError() << f1 << " " << f2 << " is same content" << endl;
    return true;
}

bool mkMultiDir(const std::string& path, int mode)
{
    char temp[256];
    strcpy(temp, path.c_str());
    dirname(temp);
    if (!existsFile(temp)) {
        if (!mkMultiDir(temp, mode)) {
            return false;
        }
    }
    if (isDirPath(path)) {
        return true;
    }
    return mkdir(path.c_str(), mode) == 0;
}

bool cp(const std::string& src, const std::string& dst) 
{
    if (src == dst) {
        Log::instance().getError() << "same file" << endl;
        return false;
    }
    fstream rfs(src, ios::in | ios::binary); //argv[1] 原文件
    fstream wfs; //argv[2] 目标文件,进行一定的判断后再打开
    defer(
        rfs.close();
    );
    // 如果dst是个目录
    if (isDirPath(dst)) {
        string filename = getOriginFilename(src);
        // 如果该目录就是src的目录
        if (IsSameFile(dst.c_str(), getDirName(src).c_str())) {
            Log::instance().getError() << src << " " << dst << " is same dir" << endl;
            return false;
        }
        wfs.open(osJoin(dst, filename), ios::out|ios::binary | ios::trunc);
    // 如果dst是个文件名，且和src属于同一个文件
    } else if (IsSameFile(src.c_str(), dst.c_str())) {
        Log::instance().getError() << src << " " << dst << " is same file" << endl;
        return false;
    // 不属于同一个文件
    } else {
        wfs.open(dst, ios::out|ios::binary | ios::trunc);
    }
    defer(
        wfs.close();
    );
    if(!wfs.good()) {
        Log::instance().getError() << "dst file can't open" << endl;
        return false;
    }
    if(!rfs.good()) {
        Log::instance().getError() << "src file can't open" << endl;
        return true;
    }

    const int COPY_BUFF_SIZE = 1024;
    char buf[COPY_BUFF_SIZE] = {};
    
    while(!rfs.eof()) { 
        rfs.read(buf,COPY_BUFF_SIZE);
        wfs.write(buf,rfs.gcount());
    }

    return false;
}

std::string replace(std::string s, std::string old_s, std::string new_s, int n)
{
    int pre = 0, now;
    string res = "";
    while (pre < s.length() && (now = s.find(old_s, pre)) != -1 && n)
    {
        res += s.substr(pre, now - pre);
        res += new_s;
        pre = now + old_s.length();
        n--;
    }
    res += s.substr(pre);
    return res;
}

std::vector<std::string> split(std::string s, std::string flag)
{
    std::vector<std::string> res;
    if (flag == "")
    {
        bool f = true;
        for (int i = 0; i < s.length();i++)
        {
            if(isspace(s[i]))
                f = true;
            else
            {
                if(f)
                {
                    res.push_back("");
                    f = false;
                }
                res.back().append(1,s[i]);
            }
        }
        return res;
    }
    int pre = 0, x;
    while (true)
    {
        x = s.find(flag, pre);
        if (x == -1)
        {
            res.push_back(s.substr(pre));
            break;
        }
        else
        {
            res.push_back(s.substr(pre, x - pre));
            pre = x + flag.length();
        }
    }
    return res;
}

bool startWith(const string& a, const string& b)
{
    int n = a.size();
    int m = b.size();
    if (n < m)
        return false;
    for (int i = 0; i < m; i++)
    {
        if (a[i] != b[i])
            return false;
    }
    return true;
}

bool endWith(const string& a, const string& b)
{
    int n = a.size();
    int m = b.size();
    if (n < m)
        return false;
    for (int i = 0; i < m; i++)
    {
        if (a[n - 1 - i] != b[m - 1 - i])
            return false;
    }
    return true;
}

void fClose()
{
#if defined(WIN32) || defined(WIN64)
    freopen("CON", "w", stderr);
    freopen("CON", "w", stdout);
    freopen("CON", "r", stdin);
#else
    freopen("/dev/tty", "w", stderr);
    freopen("/dev/tty", "w", stdout);
    freopen("/dev/tty", "r", stdin);
#endif
    // cout << "redirect console ok" << endl;
}

void fRedirectAppend(std::string inFile, std::string outFile, std::string errorFile)
{
    // cout << inFile << " " << outFile << " " << errorFile << endl;
    if (errorFile != "")
        if (freopen(errorFile.c_str(), "a+", stderr) == NULL)
        {
            Log::instance().getError() << errorFile << " open a_mode error " << endl;
            exit(SYS_ERROR);
        }
    if (outFile != "")
        if (freopen(outFile.c_str(), "a+", stdout) == NULL)
        {
            Log::instance().getError() << outFile << " open a_mode error " << endl;
            exit(SYS_ERROR);
        }
    if (inFile != "")
        if (freopen(inFile.c_str(), "r+", stdin) == NULL)
        {
            Log::instance().getError() << inFile << " open r_mode error " << endl;
            exit(SYS_ERROR);
        }
}

void fRedirectWrite(std::string inFile, std::string outFile, std::string errorFile)
{
    // cout << inFile << " " << outFile << " " << errorFile << endl;
    if (errorFile != "")
        if (freopen(errorFile.c_str(), "w+", stderr) == NULL)
        {
            Log::instance().getError() << errorFile << " open w_mode error " << endl;
            exit(SYS_ERROR);
        }
    if (outFile != "")
        if (freopen(outFile.c_str(), "w+", stdout) == NULL)
        {
            Log::instance().getError() << outFile << " open w_mode error " << endl;
            exit(SYS_ERROR);
        }
    if (inFile != "")
        if (freopen(inFile.c_str(), "r+", stdin) == NULL)
        {
            Log::instance().getError() << inFile << " open r_mode error " << endl;
            exit(SYS_ERROR);
        }
}

std::string commandAppendIOE(std::string command, std::string inFile, std::string outFile, std::string errorFile)
{
    if (outFile != "")
        command += " 1>" + outFile;
    if (errorFile != "")
    {
        if(outFile == errorFile)
            command += " 2>&1";
        else
            command += " 2>" + errorFile;        
    }
    if (inFile != "")
        command += " <" + errorFile;
    return command;
}

int getLastUpdateTime(string filename)
{
    if (existsFile(filename)) {
        struct stat buf;
        stat(filename.c_str(), &buf);
        return buf.st_mtime;
    } else {
        // 不存在此文件则返回最大值
        return INT_MAX;
    }
}

bool existsFile(std::string filename)
{
    return access(filename.c_str(), 0) != -1;
}

int getFileSize(string filename)
{
    struct stat buf;
    stat(filename.c_str(), &buf);
    return buf.st_size;
}

std::string fileToStr(std::string filename, unsigned int size)
{
    ifstream infile;
    infile.open(filename);
    if (!infile.is_open())
    {
        Log::instance().getError() << "open ifstream " << filename << " error" << endl;
        return "";
    }
    stringstream in;
    in << infile.rdbuf();
    infile.close();
    string res = in.str();
    if (size > 0 && res.length() > size)
        return res.substr(0,size) + "...省略" + to_string(res.length() - size) + "个字符";
    return res;
}

int strToFile(std::string filename, std::string data, std::ios_base::openmode flag)
{
    ofstream out;
    auto options = ios::out;
    out.open(filename, flag);
    if (!out.is_open())
    {
        Log::instance().getError() << "open ifstream " << filename << " error" << endl;
        return false;
    }
    out << data;
    return true;
}

std::string fileBlankToStr(std::string filename)
{
    char c;
    string res = "";
    ifstream infile;
    infile.open(filename);
    if (!infile.is_open())
    {
        Log::instance().getError() << "open ifstream " << filename << " error" << endl;
        exit(SYS_ERROR);
    }
    while ((c = infile.get()) != -1)
    {
        if (isBlank(c))
            res += c;
    }
    infile.close();
    return res;
}

std::string fileNonBlankToStr(std::string filename)
{
    char c;
    string res = "";
    ifstream infile;
    infile.open(filename);
    if (!infile.is_open())
    {
        Log::instance().getError() << "open ifstream " << filename << " error" << endl;
        exit(SYS_ERROR);
    }
    while ((c = infile.get()) != -1)
    {
        if (!isBlank(c))
            res += c;
    }
    infile.close();
    return res;
}

std::vector<std::string> fileSplitToVectorByBlank(std::string filename)
{
    string s;
    vector<string> res;
    ifstream infile;
    infile.open(filename);
    if (!infile.is_open())
    {
        Log::instance().getError() << "open ifstream " << filename << " error" << endl;
        exit(SYS_ERROR);
    }
    while (infile >> s)
    {
        res.push_back(s);
    }
    infile.close();
    return res;
}

std::vector<std::string> fileSplitToVectorByNonBlank(std::string filename)
{
    vector<string> res;
    char c;
    ifstream infile;
    infile.open(filename);
    if (!infile.is_open())
    {
        Log::instance().getError() << "open ifstream " << filename << " error" << endl;
        exit(SYS_ERROR);
    }
    res.push_back("");
    while ((c = infile.get()) != -1)
    {
        // 测试正确性用
        // if (isBlank(c))
        //     cout << '+';
        // else
        //     cout << c;
        if (isBlank(c))
        {
            res.back() += c;
        }
        else
        {
            //如果res为空或res末尾字符串长度不为0，则应该新插入一个空字符串，方便之后添加
            if (res.empty() || res.back().size() > 0)
                res.push_back("");
        }
    }
    infile.close();
    return res;
}

bool isBlank(char c)
{
    return isspace(c) || iscntrl(c);
}

int compareFloatStr(std::string a, std::string b, double eps)
{
    double x, y;
    char *pend;
    x = strtod(a.c_str(), &pend);
    //若存在字符没有被处理，则认为这段字符串无法被转为浮点数
    // printf("x:%f\n", x);
    // printf("%s", pend);
    if (strlen(pend) > 0) {
        return 0;
    }
    y = strtod(b.c_str(), &pend);
    // printf("y:%f\n", y);
    // printf("%s", pend);
    if (strlen(pend) > 0) {
        return 0;
    }
    if (isnan(x) || isnan(y) || isinf(x) || isinf(y)) {
        return 0;
    }
    return std::abs(x - y) <= eps;
}

bool isDirPath(const std::string& path)
{
    struct stat dst;
    stat(path.c_str(), &dst);
    return S_ISDIR(dst.st_mode);
}


//字符串转16进制字符串,适用于所有字符（包括中文）
std::string encodeHexString(const std::string& str) {
    return encodeHexString(str.c_str(), str.size());
}

std::string encodeHexString(const char* str, int len) {
	// 根据默认编码获取字节数组
	const std::string hexString = "0123456789abcdef";
	string sb(len << 1, '0');

	// 将字节数组中每个字节拆解成2位16进制整数
	for (int i = 0; i < len; i++) {
		sb[i << 1] = hexString.at((str[i] & 0xf0) >> 4);
		sb[(i << 1) + 1] = hexString.at((str[i] & 0x0f) >> 0);
	}
	return sb;
}
void createFileInMultiDir(const std::string& file) {
    auto filePath = std::filesystem::path(file);
    if (std::filesystem::exists(filePath)) {
        return;
    }
    auto fileParentPath = filePath.parent_path();
    if (filePath.has_parent_path() && !std::filesystem::exists(fileParentPath)) {
        std::filesystem::create_directory(fileParentPath);
    }
    if (!std::filesystem::exists(filePath)) {
        std::ofstream{file};
    }
}

#ifdef __linux__
std::string getTempDirectory(const std::string& format) {
    char temp[256] = {0};
    strcpy(temp, format.c_str());
    auto res = mkdtemp(temp);
    if (res != NULL) {
        chmod(res, 0711);
        return string(temp);
    }
    return "";
}
#endif